package hypeerweb;

import states.DownPointingNodeState;
import states.NodeState;
import states.TerminalNodeState;

/**
 * The range of nodes in the hypeerweb in which we will
 * find the insertion node.
 *
 * <br><br>Domain:
 * <br>&nbsp;&nbsp;&nbsp;&nbsp;lowerBound : Node
 * <br>&nbsp;&nbsp;&nbsp;&nbsp;upperBound : Node
 * 
 * <br><br>Invariants:
 * <br>&nbsp;&nbsp;&nbsp;&nbsp;lowerBound, upperBound &isin; HyPeerWeb
 * <br>&nbsp;&nbsp;&nbsp;&nbsp;upperBound.id < lowerBound.id
 * <br>&nbsp;&nbsp;&nbsp;&nbsp;upperBound == lowerBound if the range has not been previously shrunk
 *
 *
 * @author brinkerhoff_kendra
 *
 */
public class Range {
	/**
	 * The child of the largest node that we know of with children.
	 */
	private Node lowerBound;

	/**
	 * The smallest node that we know of without children.
	 */
	private Node upperBound;

	/**
	 * @param lb the child of the highest parent that we know of
	 * @param ub the smallest node without children that we know of
	 */
	public Range(final Node lb, final Node ub) {
		lowerBound = lb;
		upperBound = ub;
	}

	/**
	 * @param ub the new upper bound
	 */
	public final void setUpperBound(final Node ub) {
		upperBound = ub;
	}

	/**
	 * @return the upper bound
	 */
	public final Node getUpperBound() {
		return upperBound;
	}

	/**
	 * @param lb the new lower bound
	 */
	public final void setLowerBound(final Node lb) {
		lowerBound = lb;
	}

	/**
	 * @return the lower bound
	 */
	public final Node getLowerBound() {
		return lowerBound;
	}

	/**
	 * Determines the range between the lower and upper bounds.
	 *
	 * @return upperBound - lowerBound.parent ||
	 * 		   2 if lowerBound has no parent
	 */
	public final int getRange() {
			return upperBound.getWebId().getValue()
				- lowerBound.getParent().getWebId().getValue();
	}

	/**
	 * @return the (lb.parent, ub] || [lb, ub]
	 */
	public final String toString() {
		return "(" + lowerBound.getWebId()
			.getValue() + ", " + upperBound.getWebId()
			.getValue() + "]";
	}
	
	/**
     * Finds both the insertion node and the deletion node stored in a 
     * Range, where lowerBound = deletion node and upperBound = insertion
     * node.
     * 
     * @param startingNode the node to begin searching from. If 
     *        startingNode == NULL_NODE, then begin at node 0;
     */
    public static Range getNodeRange(Node startingNode) {
        if(startingNode == Node.NULL_NODE) {
            startingNode = HyPeerWeb.getSingleton().getNode(0);
        }
        
        //Find the largest node we can
        Node lowerBound = startingNode;
        
        while (lowerBound.getConnections().getLargestNeighbor() != null) {
            lowerBound = lowerBound.getConnections().getLargestNeighbor();
        }
    
        //Build the range and begin state machine
        Range range = new Range(lowerBound, lowerBound);
        //System.out.println(startingNode.getWebId().getValue() + " " + startingNode.getConnections());
        NodeState currentState = new DownPointingNodeState();
        while (!(currentState instanceof TerminalNodeState)) {
        	//System.out.println(currentState);
        	currentState = currentState.shrinkRange(range);
        	//System.out.println("Lower: " + range.getLowerBound());
        	//System.out.println("Upper: " + range.getUpperBound());
        	//System.out.println(range + " : " + currentState);
        }
        
        return range;
    }
}
